XML Generation

The preferred way to generate XML files with XDoclet is to use Jelly. There are several reasons for this:

  • Jelly will format the output nicely (as opposed to Velocity) * Jelly will ensure that the output is well-formed XML.

    If the XML is relatively simple (only one version of the DTD/Schema), and a relatively simple structure (less than 10 element types), the Jelly script can be written by hand, and xjavadoc can be accessed directly from the Jelly script to extract information from the tagged source code.

    However, if the plugin is supposed to support multiple versions of a DTD/Schema (as is the case with ejb-jar.xml), it's strongly recommended to generate the Jelly script with the jellygen plugin (which is paqrt of the XDoclet SDK).

    The XGG plugin takes one or more DTDs/Schemas/Relax NG documents as input and generates the following:

  • One Jelly script. * One java API with one java class for each element type. * One abstract subclass of XMLGenerator (which extends JellyGenerator).

    The generated Jelly script should not be modified, and it uses the generated Java API to retrieve the data to put in the generated XML.

    Once the plugin developer has generated this Java API and the Jelly script with XGG, the remaining job for the plugin developer is to create a tree structure of objects using this Java API.

    In the generated Java API there will be one bean-like class for each XML element. Some elements and attributes only apply to certain versions of the DTD/Schema that was given as input to XGG, but the generated Java API will contain a union of all elements and attributes. When the generated jelly script is invoked, it will know what version of the DTD/Schema it's currently being invoked for, and filter out any elements that are not appliccable for that version.

    This means that the plugin developer can concentrate on building the tree model that reresents the XML content, without having to worry much about what version of the DTD/Schema it's going to be used with.

    The plugin developer should extend the generated JellyGenerator subclass and implement the single method named fill( RootElement o ). RootElement is the class that corresponds to the root element for the DTD/Schema (assuming that they all have the same root element).

    The implementation of this method should use the xjavadoc API to pull information and build the tree of objects. It's recommended to implement additional private fill( SomeElement element, [XClass clazz], [XMethod method], [XTag tag], [Whatever whatever]) methods that can fill a particular element with data. (The optional arguments are typically the objects that are needed in order to fill the element.

XML validation

The XMLGenerator class augments the JellyGenerator with XML validation logic, using Sun's MSV. JellyGenerator will (optionally) invoke MSV after the generation is done, to verify that the generated XML complies with the DTD/Schema.

If MSV reports an error, it will be extremely difficult to implement an algorithm that could match this back to what @tag (or possibly Ant parameter) that was incorrect or missing. The best that can be done is to point the user to the source code and try to figure it out. -Or implement some additional validation logic in the generator.

The MSV library is quite big, and we shouldn't force people to have it installed. Therefore, we need to cope with situations where it (and its secondary libraries) are not on the classpath. There should also be a switch to turn it on and off. If it's set to on, but not on classpath, an exception should be thrown, saying "either put MSV on the classpath or turn off validation".

Documentation

All the code generated by XGG must be well documented, so it's easy to use. The plugin developer needs to know about the generated Java bean API, and the user needs to know about the generator options, such as what DTD/XSD version it supports, and how to tell which one you want.

Implementation

XGG will use Zeus as part of the generation process. Zeus has excellent support for DTDs, and we should extend it to support Relax NG (which is easier than XML Schema). If you want to use XGG with an XML Schema, it should first be converted to Relax NG using Sun's rngconv. This conversion should be done by the enhanced Zeus (if necessary) and it should remember the original Schema declaration, because that's what we want to declare in the XML that Jelly will generate.

We'll modify Zeus so that it generates simple interfaces instead of java classes as it does today. These interfaces will eventually be thrown away, and serve as an intermediate result.

Then we'll use a simplified version of the current XMLFacade plugin (which will be merged into XGG) that parses all the Zeus-generated APIs and generates a new "Union" API with concrete classes, and special tags and comments indicating what classes and methods are appliccable for what versions of the DTD/Schema.

Now we can throw away the classes Zeus generated.

With this "Union" API (and its special "version" tags) we can generate the Jelly script and the XMLGenerator subclass. We'll reuse code from the early XGG initiative for this. In stead of using Velocity to generate Velocity, we'll use Jelly to generate Jelly. This is a little bit tricky. We must use some special charaters in the generated script (so Jelly doesn't gobble them) and use Ant's replace task afterwards. That's OK, and it actually makes it easier to read the jelly generating jelly script.

The final result is a nice Union API, a Jelly script and a skeletal Generator class that knows how to deal with the different versions of the DTD/Schema, and can invoke MSV to post-validate.

The only thing the plugin developer needs to do now is to subclass the generator and fill the "Union" API with data (using xjavadoc).

This approach will reduce the size of the plugin dramatically. the ejb-jar plugin (with support for 1.1 and 2.0) was 984 KB. The new size will be 100-150 KB, which is OK.

While the internals of XGG is a bit complex, it won't be for the plugin developer, and that's the most important.

XGG should be wrapped in an Ant task so it can be easily used by Ant people. In fact, the entire XDoclet SDK should be Ant tasks!!!!!!!!!!!!!!!